﻿using System;
using System.Configuration;
using System.Data;
using System.Globalization;

using BizElements.Core;

namespace BizElements.BusinessLayer
{
    /// <summary>
    /// Entity model class for the table/view 'LockManager'.
    /// </summary>
    [Serializable]
    class LockManagerEntity : EntityModelBase
    {
        #region Private members.

        // Members mapped to database columns.
        private int? lockId;
        private string objectType;
        private string objectId;
        private string lockedBy;
        private DateTime? lockedTill;
        private string lockMode;


        #endregion

        #region Protected properties.

        // Properties mapped to database columns.    
        protected int? _LockId
        {
            get
            {
                return this.lockId;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if ((!value.HasValue != !this.lockId.HasValue) || (value.HasValue && (value.Value != this.lockId.Value)))
                        this.EntityState = EntityState.OutOfSync;
                }
                this.lockId = value;
            }
        }

        protected string _ObjectType
        {
            get
            {
                return this.objectType;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if (value != this.objectType)
                        this.EntityState = EntityState.OutOfSync;
                }
                this.objectType = value;
            }
        }

        protected string _ObjectId
        {
            get
            {
                return this.objectId;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if (value != this.objectId)
                        this.EntityState = EntityState.OutOfSync;
                }
                this.objectId = value;
            }
        }

        protected string _LockedBy
        {
            get
            {
                return this.lockedBy;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if (value != this.lockedBy)
                        this.EntityState = EntityState.OutOfSync;
                }
                this.lockedBy = value;
            }
        }

        protected DateTime? _LockedTill
        {
            get
            {
                return this.lockedTill;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if ((!value.HasValue != !this.lockedTill.HasValue) || (value.HasValue && (value.Value != this.lockedTill.Value)))
                        this.EntityState = EntityState.OutOfSync;
                }
                this.lockedTill = value;
            }
        }

        protected string _LockMode
        {
            get
            {
                return this.lockMode;
            }
            set
            {
                if (this.EntityState == EntityState.Synchronized)
                {
                    if (value != this.lockMode)
                        this.EntityState = EntityState.OutOfSync;
                }
                this.lockMode = value;
            }
        }


        #endregion

        #region Constructors.

        /// <summary>
        /// Default constructor.
        /// </summary>
        public LockManagerEntity()
            : base(new LockManagerMeta())
        {
        }

        /// <summary>
        /// Constructor. Initalizes Table property with the specified (shared) instance.
        /// </summary>
        /// <param name="dbTable">Metadata for table/view to which the entity belongs to.</param>
        public LockManagerEntity(LockManagerMeta dbTable)
            : base(dbTable)
        {
        }

        /// <summary>
        /// Constructor. Initializes primary key. Useful for methods that don't require other values (eg. DeleteOne, SelectOne).
        /// </summary>
        public LockManagerEntity(int lockId)
            : base(new LockManagerMeta())
        {
            _LockId = lockId;
        }

        /// <summary>
        /// Constructor. Initializes primary key. Useful for methods that don't require other values (eg. DeleteOne, SelectOne).
        /// </summary>
        public LockManagerEntity(LockManagerMeta dbTable, int lockId)
            : base(dbTable)
        {
            _LockId = lockId;
        }

        /// <summary>
        /// Constructor. Initializes members with the values in the given DataRow.
        /// </summary>
        /// <param name="row">DataRow with required columns.</param>
        /// <remarks>If row's state is <see cref="DataRowState.Unchanged"/> the entity's state will be set to <see cref="EntityState.Synchronized"/>. Other states are ignored.</remarks>
        public LockManagerEntity(DataRow row)
            : base(new LockManagerMeta())
        {
            FromDataRow(row);
        }

        /// <summary>
        /// Constructor. Initializes members with the values in the given DataRow.
        /// </summary>
        /// <param name="dbTable">Metadata for table/view to which the entity belongs to.</param>
        /// <param name="row">DataRow with required columns.</param>
        /// <remarks>If row's state is <see cref="DataRowState.Unchanged"/> the entity's state will be set to <see cref="EntityState.Synchronized"/>. Other states are ignored.</remarks>
        public LockManagerEntity(LockManagerMeta dbTable, DataRow row)
            : base(dbTable)
        {
            FromDataRow(row);
        }

        /// <summary>
        /// Copy constructor.
        /// </summary>
        /// <param name="existing">Instance of LockManagerEntity.</param>
        public LockManagerEntity(LockManagerEntity existing)
            : base(existing.Table)
        {
            FromExistingEntity(existing);
        }

        static readonly Type DbTableClass = typeof(LockManagerMeta);

        /// <summary>
        /// Initializes all members of LockManagerEntity and base EntityModelBase class. This is the fastest way to initialize entity with data from database.
        /// </summary>
        private LockManagerEntity(IDbTable table, EntityState entityState, int? lockId, string objectType, string objectId, string lockedBy, DateTime? lockedTill, string lockMode)
            : base(table, DbTableClass, entityState)
        {
            this.lockId = lockId;
            this.objectType = objectType;
            this.objectId = objectId;
            this.lockedBy = lockedBy;
            this.lockedTill = lockedTill;
            this.lockMode = lockMode;
        }

        /// <summary>
        /// Initializes all members mapped to fields.
        /// </summary>
        protected void Init(int? lockId, string objectType, string objectId, string lockedBy, DateTime? lockedTill, string lockMode)
        {
            _LockId = lockId;
            _ObjectType = objectType;
            _ObjectId = objectId;
            _LockedBy = lockedBy;
            _LockedTill = lockedTill;
            _LockMode = lockMode;
        }

        #endregion

        #region Methods.

        /// <summary>
        /// Gets the value(s) that uniquely identifiy an entity.
        /// In the order as specified in accompanying IDbTable metadata class.
        /// NULL if the parent table/view doesn't have a primary key constraint or the required fields are not set.
        /// </summary>
        public override object[] GetPrimaryKeyValue()
        {
            if (_LockId != null)
                return new object[] { _LockId.Value };
            else
                return null;
        }

        /// <summary>
        /// Initializes entity members with data stored in the given DataRow.
        /// </summary>
        /// <param name="row">DataRow with the required columns.</param>
        /// <remarks>If row's state is <see cref="DataRowState.Unchanged"/> the entity's state will be set to <see cref="EntityState.Synchronized"/>. Other states are ignored.</remarks>
        public override void FromDataRow(DataRow row)
        {
            object currentColumnValue;

            currentColumnValue = TryGetColumnValue(row, "LockId");
            _LockId = (currentColumnValue != DBNull.Value) ? (int?)Convert.ToInt32(currentColumnValue) : null;

            currentColumnValue = TryGetColumnValue(row, "ObjectType");
            _ObjectType = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumnValue = TryGetColumnValue(row, "ObjectId");
            _ObjectId = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumnValue = TryGetColumnValue(row, "LockedBy");
            _LockedBy = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumnValue = TryGetColumnValue(row, "LockedTill");
            _LockedTill = (currentColumnValue != DBNull.Value) ? (DateTime?)Convert.ToDateTime(currentColumnValue) : null;

            currentColumnValue = TryGetColumnValue(row, "LockMode");
            _LockMode = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            bool setEntityStateToSynchronized = (row.RowState == DataRowState.Unchanged);
            if (setEntityStateToSynchronized)
                this.EntityState = EntityState.Synchronized;
        }

        private static object TryGetColumnValue(DataRow row, string columnName)
        {
            int colIdx = row.Table.Columns.IndexOf(columnName);
            return (colIdx >= 0) ? row[colIdx] : null;
        }

        private static object GetColumnValue(DataRow row, IDbColumn column)
        {
            int colIdx = row.Table.Columns.IndexOf(column.Alias);
            if (colIdx < 0)
                colIdx = row.Table.Columns.IndexOf(column.ColumnName);
            if (colIdx < 0)
                throw new ArgumentException("DataTable doesn't contain the specified column (" + column.ColumnName + ").");

            return row[colIdx];
        }

        /// <summary>
        /// Initializes entity members with data stored in the given DataRow.
        /// </summary>
        /// <param name="row">DataRow with all or some of the columns defined in meta data.</param>
        /// <param name="fieldMetaData"><see cref="IDbTable"/> meta data object which links ADO.NET row columns to entity properties.</param>
        /// <remarks>If row's state is <see cref="DataRowState.Unchanged"/> the entity's state will be set to <see cref="EntityState.Synchronized"/>. Other states are ignored.</remarks>
        public override void FromDataRow(DataRow row, IDbTable fieldMetaData)
        {
            IDbColumn currentColumn;
            object currentColumnValue;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.LockId.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _LockId = (currentColumnValue != DBNull.Value) ? (int?)Convert.ToInt32(currentColumnValue) : null;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.ObjectType.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _ObjectType = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.ObjectId.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _ObjectId = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.LockedBy.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _LockedBy = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.LockedTill.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _LockedTill = (currentColumnValue != DBNull.Value) ? (DateTime?)Convert.ToDateTime(currentColumnValue) : null;

            currentColumn = fieldMetaData.Columns.GetByPropertyName(this.Table.LockMode.PropertyName);
            currentColumnValue = (currentColumn != null) ? GetColumnValue(row, currentColumn) : DBNull.Value;
            _LockMode = (currentColumnValue != DBNull.Value) ? (string)Convert.ToString(currentColumnValue) : null;

            bool setEntityStateToSynchronized = (row.RowState == DataRowState.Unchanged);
            if (setEntityStateToSynchronized)
                this.EntityState = EntityState.Synchronized;
        }

        /// <summary>
        /// Converts the given DataRow to LockManagerEntity.
        /// </summary>
        /// <param name="row">DataRow with required columns.</param>
        /// <remarks>If row's state is <see cref="DataRowState.Unchanged"/> the entity's state will be set to <see cref="EntityState.Synchronized"/>. Other states are ignored.</remarks>
        public static explicit operator LockManagerEntity(DataRow row)
        {
            return new LockManagerEntity(row);
        }

        /// <summary>
        /// Creates an array of objects containing entity data.
        /// </summary>
        /// <returns>Entity values.</returns>
        public override object[] ToObjectArray()
        {
            object[] values = new object[6];

            values[0] = (_LockId != null) ? (object)_LockId.Value : null;
            values[1] = (_ObjectType != null) ? (object)_ObjectType : null;
            values[2] = (_ObjectId != null) ? (object)_ObjectId : null;
            values[3] = (_LockedBy != null) ? (object)_LockedBy : null;
            values[4] = (_LockedTill != null) ? (object)_LockedTill.Value : null;
            values[5] = (_LockMode != null) ? (object)_LockMode : null;

            return values;
        }

        /// <summary>
        /// Initializes entity members with the given values.
        /// </summary>
        /// <param name="entityValues">Array with the required values.</param>
        public override void FromObjectArray(object[] entityValues)
        {
            _LockId = (entityValues[0] != null) ? (int?)Convert.ToInt32(entityValues[0]) : null;
            _ObjectType = (entityValues[1] != null) ? (string)Convert.ToString(entityValues[1]) : null;
            _ObjectId = (entityValues[2] != null) ? (string)Convert.ToString(entityValues[2]) : null;
            _LockedBy = (entityValues[3] != null) ? (string)Convert.ToString(entityValues[3]) : null;
            _LockedTill = (entityValues[4] != null) ? (DateTime?)Convert.ToDateTime(entityValues[4]) : null;
            _LockMode = (entityValues[5] != null) ? (string)Convert.ToString(entityValues[5]) : null;
        }

        /// <summary>
        /// Gets the parent entity defined by the given foreign key.
        /// </summary>
        /// <param name="foreignKey">FK which must be defined in the LockManagerMeta class or an exception is generated.</param>
        /// <returns>Parent entity. NULL if the FK fields haven't been set or if the entity with the given key values doesn't exist.</returns>
        public override IEntity GetParent(DbRelation foreignKey)
        {
            throw new Exception("No foreign keys are defined in the LockManagerMeta class.");
        }

        /// <summary>
        /// Sets the given value into the member that represents the parent entity defined by the foreign key.
        /// </summary>
        /// <param name="foreignKey">FK which must be defined in the CFunctionsMeta class or an exception is generated.</param>
        /// <param name="entity">Parent entity. May be NULL. Must be an instance of the CFunctionsEntity or a derived class.</param>
        public override void SetParent(DbRelation foreignKey, IEntity entity)
        {
            throw new Exception("No foreign keys are defined in the LockManagerMeta class.");
        }

        #endregion

        /// <summary>
        /// Gets typed IDbTable object for the entity's table/view.
        /// </summary>
        public LockManagerMeta Table
        {
            get { return (LockManagerMeta)_Table; }
        }

        #region Public properties mapped to database columns.

        /// <summary>
        /// Gets or sets the value which is mapped to the non-nullable field 'LockId'.
        /// If null-check is enabled a NoNullAllowedException is thrown if getter is used before the value has been set.
        /// </summary>
        public virtual int LockId
        {
            get
            {
                if (_LockId == null)
                {
                    if (this.NullCheckEnabled)
                        throw new NoNullAllowedException("LockManagerEntity.get_LockId: LockId is not set yet.");
                    else
                        return default(int);
                }

                return _LockId.Value;
            }
            set
            {
                _LockId = value;
            }
        }

        /// <summary>
        /// Gets or sets the value which is mapped to the non-nullable field 'ObjectType'.
        /// </summary>
        public virtual string ObjectType
        {
            get
            {
                return _ObjectType;
            }
            set
            {
                _ObjectType = value;
            }
        }

        /// <summary>
        /// Gets or sets the value which is mapped to the non-nullable field 'ObjectId'.
        /// </summary>
        public virtual string ObjectId
        {
            get
            {
                return _ObjectId;
            }
            set
            {
                _ObjectId = value;
            }
        }

        /// <summary>
        /// Gets or sets the value which is mapped to the nullable field 'LockedBy'.
        /// </summary>
        public virtual string LockedBy
        {
            get
            {
                return _LockedBy;
            }
            set
            {
                _LockedBy = value;
            }
        }

        /// <summary>
        /// Gets or sets the value which is mapped to the nullable field 'LockedTill'.
        /// </summary>
        public virtual DateTime? LockedTill
        {
            get
            {
                return _LockedTill;
            }
            set
            {
                _LockedTill = value;
            }
        }

        /// <summary>
        /// Gets or sets the value which is mapped to the nullable field 'LockMode'.
        /// </summary>
        public virtual string LockMode
        {
            get
            {
                return _LockMode;
            }
            set
            {
                _LockMode = value;
            }
        }

        #endregion

        #region NewEntity static methods suitable for usage in EntityBuilder<T>.

        /// <summary>Creates new entity and initializes all members of with the given values.</summary>
        /// <param name="table">Metadata for table/view to which the entity belongs to.</param>
        /// <param name="entityState">Indicates the state of entity with regard to data-source.</param>
        /// <param name="values">Array which contains values for all properties mapped to database columns in the following order: LockId, ObjectType, ObjectId, LockedBy, LockedTill, LockMode.</param>
        /// <remarks>This is the fastest method to initialize an entity as it directly initializes all members of base and derived class, 
        /// skips all validation checks and doesn't attempt to convert provided value data types. The method is typically used in combination 
        /// with IDataConverter objects which retrieve property values directly from IDataReader objects.</remarks>
        public static LockManagerEntity NewEntity(IDbTable table, EntityState entityState, object[] values)
        {
            return new LockManagerEntity(table, entityState, (int)values[0], (string)values[1], (string)values[2], (string)ReplaceDbNull(values[3]), CastTo<DateTime>(values[4]), (string)ReplaceDbNull(values[5]));
        }

        /// <summary>Creates new entity and initializes members for which values are defined in the array.</summary>
        /// <param name="table">Metadata for table/view to which the entity belongs to.</param>
        /// <param name="entityState">Indicates the state of entity with regard to data-source.</param>
        /// <param name="values">Array which contains values or nulls for all properties mapped to database columns in the following order: LockId, ObjectType, ObjectId, LockedBy, LockedTill, LockMode.</param>
        /// <remarks>This is the fastest method to initialize an entity as it directly initializes all members of base and derived class, 
        /// skips all validation checks and doesn't attempt to convert provided value data types. The method is typically used in combination 
        /// with IObjectReader objects which retrieve property values directly from IDataReader objects.</remarks>
        public static LockManagerEntity NewPartialEntity(IDbTable table, EntityState entityState, object[] values)
        {
            return new LockManagerEntity(table, entityState, CastTo<int>(values[0]), (string)ReplaceDbNull(values[1]), (string)ReplaceDbNull(values[2]), (string)ReplaceDbNull(values[3]), CastTo<DateTime>(values[4]), (string)ReplaceDbNull(values[5]));
        }

        private static T? CastTo<T>(object val)
            where T : struct
        {
            if (IsNull(val))
                return null;
            else
                return (T)val;
        }

        private static bool IsNull(object val)
        {
            return (val == DBNull.Value || val == null);
        }

        private static object ReplaceDbNull(object dbValue)
        {
            return (dbValue != DBNull.Value) ? dbValue : null;
        }

        #endregion
    }
}
